﻿module TinyJava.TAC

open TinyJava.Ast
open Environment
open TypeRules
open System.IO

type Id = string

type Operator =
| Add of Id * Id
| StrCat of Id * Id
| Sub of Id * Id
| Mul of Id * Id
| Div of Id * Id
| Mod of Id * Id
| Less of Id * Id
| Leq of Id * Id
| Greater of Id * Id
| Geq of Id * Id
| Eq of Id * Id
| Neq of Id * Id
| Sign of Id
| Not of Id
| Length of Id
| NewObject of Id
| NewArray of SynType * Id

with override this.ToString() =
        match this with
        | Add(t1,t2) -> t1 + " + " + t2
        | StrCat(t1,t2) -> t1 + " ++ " + t2
        | Sub(t1,t2) -> t1 + " - " + t2
        | Mul(t1,t2) -> t1 + " * " + t2
        | Div(t1,t2) -> t1 + " / " + t2
        | Mod(t1,t2) -> t1 + " % " + t2
        | Less(t1,t2) -> t1 + " < " + t2
        | Leq(t1,t2) -> t1 + " <= " + t2
        | Greater(t1,t2) -> t1 + " > " + t2
        | Geq(t1,t2) -> t1 + " >= " + t2
        | Eq(t1,t2) -> t1 + " == " + t2
        | Neq(t1,t2) -> t1 + " != " + t2
        | Sign(t) -> "-" + t
        | Not(t) -> "!" + t
        | Length(t) -> t + ".length"
        | NewObject(t) -> "new " + t
        | NewArray(typ,t) ->
            match typ with
            | SynType.Int ->
                "new int[" + t + "]"
            | SynType.Boolean ->
                "new bool[" + t + "]"
            | SynType.String ->
                "new string[" + t + "]"
            | SynType.Identifier(id) ->
                "new " + id + "[" + t + "]"

//NOTE: left argument of Assign can be a TAC temp, a block variable or a class field.
type MemoryOp =
| Assign of Id * Id //t = id, id = t
| AssignOp of Id * Operator // t = t1 op t2, t = op t1
| AssignLit of Id * Literal // t = literal
| ArrayAccess of Id * Id * Id // t = t1[t2]
| FieldAccess of Id * Id * Id // t = t1.t2
| FieldAssignment of Id * Id * Id // t1.t2 = t
| ArrayAssignment of Id * Id * Id // t1[t2] = t

with override this.ToString() =
      let assign_string t = t + " = "
      match this with
      | Assign(t,id) ->
          (assign_string t) + id
      | AssignOp(t,op) ->
          (assign_string t) + (op.ToString())
      | AssignLit(t,lit) ->
          match lit with
          | IntLit(i) ->
              (assign_string t) + (string i)
          | StringLit(s) ->
              (assign_string t) + "\"" + s + "\""
          | Boolean(b) ->
              (assign_string t) + (string b)
          | Null ->
              (assign_string t) + "null"
      | ArrayAccess(t,t1,t2) ->
          (assign_string t) + t1 + "[" + t2 + "]"
      | FieldAccess(t,t1,t2) ->
          (assign_string t) + t1 + "." + t2
      | FieldAssignment(t1,t2,t) ->
          t1 + "." + t2 + " = " + t
      | ArrayAssignment(t1,t2,t) ->
          t1 + "[" + t2 + "] = " + t
      

type Label = Label of string

with override this.ToString() = 
      match this with
      | Label(s) -> s

type TACStm =
| MemoryOp of MemoryOp //various assignment and access operations
| Label of Label //label
| Jump of Label //jump to label
| TJump of Id * Label //jump if t is true
| FJump of Id * Label //jump if t is false
| Call of Id * Option<Id> * Id * (Id list) // t = t0.id(t1,...,tn)
| VoidCall of Option<Id> * Id * (Id list) // t0.id(t1,...,tn)
| Return of Id // return t
| VoidReturn //return
| Comment of string //comments used for debugging

with override this.ToString() =
      let rec actuals_string actuals res first =
        match actuals with
        | [] -> "()"
        | [act] ->
            if first then
              "(" + act + ")"
            else
              res + act + ")"
        | act :: acts ->
            if first then
              actuals_string acts ("(" + act + ",") false
            else
              actuals_string acts (res + act + ",") false
      match this with
      | MemoryOp(m_op) ->
          m_op.ToString()
      | Label(l) -> 
          l.ToString()
      | Jump(l) ->
          "jump " + l.ToString()
      | TJump(t,l) ->
          "tjump " + t + " " + l.ToString()
      | FJump(t,l) ->
          "fjump " + t + " " + l.ToString()
      | Call(t,t0,id,actuals) ->
          match t0 with
          | None ->
              t + " = " + id + (actuals_string actuals "" true)
          | Some t0 ->
              t + " = " + t0 + "." + id + (actuals_string actuals "" true)
      | VoidCall(t0,id,actuals) ->
          match t0 with
          | None ->
              id + (actuals_string actuals "" true)
          | Some t0 ->
              t0 + "." + id + (actuals_string actuals "" true)
      | Return(t) ->
          "return " + t
      | VoidReturn ->
          "return"
      | Comment(s) ->
          "# " + s

type TACGenerator =
  {
    MethodName                    : string
    GeneratedCode                 : TACStm list
    TempList                      : Id list
    LabelList                     : Label list
    mutable LabelIndex            : int
    mutable TempIndex             : int
    mutable LastTemp              : Id
    mutable LastLabel             : Label
    mutable LastLoopBeginLabel    : Label
    mutable LastLoopEndLabel      : Label
  }

type TACContainer =
  {
    ClassName  : string
    MethodsTAC : TACGenerator list
  }

let temp_prefix = "**TJTacTemp_"

let generate_exceptions (g : TACGenerator) : TACGenerator =
  let null_pointer = Label.Label("NULL_POINTER_EXCEPTION")
  let out_of_bound = Label.Label("ARRAY_OUT_OF_BOUND_EXCEPTION")
  {
    g with 
      GeneratedCode = g.GeneratedCode @ [Label(null_pointer);Label(out_of_bound)]
      LabelList = g.LabelList @ [null_pointer;out_of_bound]
  }

let add_tac_stm (g : TACGenerator) (stm : TACStm) : TACGenerator =
  {g with GeneratedCode = g.GeneratedCode @ [stm]}

let rec add_tac_stms (g : TACGenerator) (stms : TACStm list) : TACGenerator =
  match stms with
  | [] -> g
  | stm :: stms ->
      let g = add_tac_stm g stm
      add_tac_stms g stms


let add_comment (g : TACGenerator) (comment : string) =
  add_tac_stm g (Comment(comment))

let add_temp (g : TACGenerator) : TACGenerator =
  let prefix = temp_prefix
  let new_temp = prefix + (string g.TempIndex)
  g.TempIndex <- g.TempIndex + 1
  g.LastTemp <- new_temp
  {g with TempList = g.TempList @ [new_temp]}

let add_label (g : TACGenerator) : TACGenerator =
  let prefix = "Label"
  let new_label = Label.Label("Label" + (string g.LabelIndex))
  g.LabelIndex <- g.LabelIndex + 1
  g.LastLabel <-new_label
  { g with LabelList = g.LabelList @ [new_label]}

let rec out_of_bound_exc (t0 : Id) (t1 : Id) : TinyJava.Ast.Expression =
  //generate TinyJava AST for t1 >= 0
  let exc_test_grt = 
    TinyJava.Ast.BinaryOp(TinyJava.Ast.Location(TinyJava.Ast.Id(t1)),TinyJava.Ast.Geq,TinyJava.Ast.Literal(TinyJava.Ast.IntLit(0)))
  //generate TinyJava AST for t1 < t0.length
  let exc_test_less =
    TinyJava.Ast.BinaryOp(TinyJava.Ast.Location(TinyJava.Ast.Id(t1)),TinyJava.Ast.Less,TinyJava.Ast.Length(TinyJava.Ast.Location(TinyJava.Ast.Id(t0))))
  //generate TinyJava AST for t1 >= 0 && t1 < t0.length
  let out_of_bound_ast =
    TinyJava.Ast.BinaryOp(exc_test_grt,TinyJava.Ast.And,exc_test_less)
  out_of_bound_ast

and null_pointer_exc (t0 : Id) : TinyJava.Ast.Expression =
    TinyJava.Ast.BinaryOp(TinyJava.Ast.Location(TinyJava.Ast.Id(t0)),
                                                TinyJava.Ast.Eq,
                                                TinyJava.Ast.Literal(TinyJava.Ast.Null))

//translation rules from TinyJava AST to TAC.

// actuals translation
and map_actuals (actuals : TinyJava.Ast.Actuals) (g : TACGenerator) (last_temps : Id list) : TACGenerator * (Id list) =
  match actuals with
  | [] -> g,last_temps
  | a :: acs ->
      let g = add_temp g
      let temp = g.LastTemp
      let g = map_expression a g temp
      map_actuals acs g (last_temps @ [temp])


// expression translation
and map_expression (e : TinyJava.Ast.Expression) (g : TACGenerator) (t : Id) : TACGenerator =
  match e with
  | TinyJava.Ast.Expression.UnaryOp(u_op,exp) ->
      let g = map_expression exp g t
      match u_op with
      | TinyJava.Ast.Sign ->
          let g = add_comment g "t = -exp"
          let g = map_expression exp g t
          add_tac_stm g (MemoryOp(AssignOp(t,Sign(t))))
      | TinyJava.Ast.Not ->
          let g = add_comment g "t = !exp"
          let g = map_expression exp g t
          add_tac_stm g (MemoryOp(AssignOp(t,Not(t))))
  | TinyJava.Ast.Expression.BinaryOp(e1,b_op,e2) ->
      match b_op with
      | TinyJava.Ast.Plus(op_t) ->
          match !op_t with
          | TinyJava.Ast.SynType.Int ->
              let g = add_comment g "t = e1 + e2"
              let g = map_expression e1 g t
              let g = add_temp g
              let t1 = g.LastTemp
              let g = map_expression e2 g t1
              add_tac_stm g (MemoryOp(AssignOp(t,Add(t,t1))))
          | TinyJava.Ast.SynType.String ->
              let g = add_comment g "t = e1 + e2"
              let g = map_expression e1 g t
              let g = add_temp g
              let t1 = g.LastTemp
              let g = map_expression e2 g t1
              add_tac_stm g (MemoryOp(AssignOp(t,StrCat(t,t1))))
          | _ ->
              failwith "BUG in TAC: Expected Integer or String as return type of Plus"
      | TinyJava.Ast.Minus ->
          let g = add_comment g "t = e1 - e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Sub(t,t1))))
      | TinyJava.Ast.Mul ->
          let g = add_comment g "t = e1 * e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Mul(t,t1))))
      | TinyJava.Ast.Div ->
          let g = add_comment g "t = e1 / e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Div(t,t1))))
      | TinyJava.Ast.Mod ->
          let g = add_comment g "t = e1 % e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Mod(t,t1))))
      | TinyJava.Ast.And ->
          let g = add_comment g "t = e1 && e2"
          let g = map_expression e1 g t
          let g = add_label g
          let label = g.LastLabel
          let jump = FJump(t,label)
          let g = add_tac_stm g jump
          let g = map_expression e2 g t
          add_tac_stm g (Label(label))
      | TinyJava.Ast.Or ->
          let g = add_comment g "t = e1 || e2"
          let g = map_expression e1 g t
          let g = add_label g
          let label = g.LastLabel
          let jump = TJump(t,label)
          let g = add_tac_stm g jump
          let g = map_expression e2 g t
          add_tac_stm g (Label(label))
      | TinyJava.Ast.Less ->
          let g = add_comment g "t = e1 < e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Less(t,t1))))
      | TinyJava.Ast.Leq ->
          let g = add_comment g "t = e1 <= e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Leq(t,t1))))
      | TinyJava.Ast.Greater ->
          let g = add_comment g "t = e1 > e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Greater(t,t1))))
      | TinyJava.Ast.Geq ->
          let g = add_comment g "t = e1 >= e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Geq(t,t1))))
      | TinyJava.Ast.Eq ->
          let g = add_comment g "t = e1 == e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Eq(t,t1))))
      | TinyJava.Ast.Neq ->
          let g = add_comment g "t = e1 != e2"
          let g = map_expression e1 g t
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression e2 g t1
          add_tac_stm g (MemoryOp(AssignOp(t,Neq(t,t1))))
  | TinyJava.Ast.Location(l) ->
      match l with
      | TinyJava.Ast.Id(id) ->
          let g = add_comment g "t = id"        
          add_tac_stm g (MemoryOp(Assign(t,id)))
      | TinyJava.Ast.ClassLoc(exp,id) ->
          let g = add_comment g "t = e.id"
          let g = add_temp g
          let t0 = g.LastTemp
          let g = add_temp g
          let t1 = g.LastTemp
          let g = map_expression exp g t0
          let null_pointer_check = null_pointer_exc t0
          let g = map_expression null_pointer_check g t1
          let tjump = TJump(t1,Label.Label("NULL_POINTER_EXCEPTION"))
          let acs = MemoryOp(FieldAccess(t,t0,id))
          add_tac_stms g [tjump;acs]

      | TinyJava.Ast.ArrayLoc(e0,e1) ->
          let g = add_comment g "t = e0[e1]"
          let g = add_temp g
          let t0 = g.LastTemp
          let g = add_temp g
          let t1 = g.LastTemp
          let g = add_temp g
          let t2 = g.LastTemp
          let g = map_expression e0 g t0
          let g = map_expression e1 g t1
          let out_of_bound_check = out_of_bound_exc t0 t1
          let g = map_expression out_of_bound_check g t2
          let fjump = FJump(t2,Label.Label("ARRAY_OUT_OF_BOUND_EXCEPTION"))
          let acs = MemoryOp(ArrayAccess(t,t0,t1))
          add_tac_stms g [fjump;acs]

  | TinyJava.Ast.Expression.Call(m,actuals_opt) ->
      match m,actuals_opt with
      | ImplicitMethod(id),Some actuals ->
          let g,temps = map_actuals actuals g []
          if is_library_function id then
            let g = add_comment g "t = f(e1,...,en)"
            add_tac_stm g (Call(t,None,id,temps))
          else
            let g = add_comment g "t = this.m(e1,...,e2)"
            add_tac_stm g (Call(t,Some "this",id,"this" :: temps))
      | ImplicitMethod(id),None ->
          if is_library_function id then
            let g = add_comment g "t = f()"
            add_tac_stm g (Call(t,None,id,[]))
          else
            let g = add_comment g "t = this.m()"
            add_tac_stm g (Call(t,Some "this",id,["this"]))
      | MethodCall(e,id), Some actuals ->
          let g = add_comment g "t = id.m(e1,...,en)"
          let g = add_temp g
          let t0 = g.LastTemp
          let g = add_temp g
          let tp = g.LastTemp
          let g = map_expression e g t0
          let null_pointer_check = null_pointer_exc t0
          let g = map_expression null_pointer_check g tp
          let tjump = TJump(tp,Label.Label("NULL_POINTER_EXCEPTION"))
          let g,temps = map_actuals actuals g []
          let call = Call(t,Some t0,id,"this" :: temps)
          add_tac_stms g [tjump;call]
      | MethodCall(e,id), None ->
          let g = add_comment g "t = id.m()"
          let g = add_temp g
          let t0 = g.LastTemp
          let g = add_temp g
          let tp = g.LastTemp
          let g = map_expression e g t0
          let null_pointer_check = null_pointer_exc t0
          let g = map_expression null_pointer_check g tp
          let tjump = TJump(tp,Label.Label("NULL_POINTER_EXCEPTION"))
          let call = Call(t,Some t0,id,["this"])
          add_tac_stms g [tjump;call]

    | TinyJava.Ast.This ->
        let g = add_comment g "t = this"
        add_tac_stm g (MemoryOp(Assign(t,"this")))

    | TinyJava.Ast.Length(e) ->
        let g = add_comment g "t = e.length"
        let g = map_expression e g t
        add_tac_stm g (MemoryOp(AssignOp(t,Length(t))))
    
    | TinyJava.Ast.DefaultConstructor(id) ->
        let g = add_comment g "t = new T()"
        add_tac_stm g (MemoryOp(AssignOp(t,NewObject(id))))

    | TinyJava.Ast.ArrayConstructor(typ,e) ->
        let g = add_comment g "t = new T[e]"
        let g = add_temp g
        let t0 = g.LastTemp
        let g = map_expression e g t0
        add_tac_stm g (MemoryOp(AssignOp(t,NewArray(typ,t0))))

    | TinyJava.Ast.Literal(l) ->
        match l with
        | IntLit(_) as i ->
            let g = add_comment g "t = int_literal"
            add_tac_stm g (MemoryOp(AssignLit(t,i)))

        | StringLit(_) as st ->
            let g = add_comment g "t = string_lit"
            add_tac_stm g (MemoryOp(AssignLit(t,st)))

        | Boolean(_) as b ->
            let g = add_comment g "t = boolean_lit"
            add_tac_stm g (MemoryOp(AssignLit(t,b)))

        | Null as n ->
            let g = add_comment g "t = null"
            add_tac_stm g (MemoryOp(AssignLit(t,n)))

// statement translation
and map_statement (s : TinyJava.Ast.Statement) (g : TACGenerator) =
  match s with
  | TinyJava.Ast.Statement.Assign(l,e0) ->
      match l with
      | Id(id) ->
          let g = add_comment g "id = e0"
          let g = add_temp g
          let t = g.LastTemp
          let g = map_expression e0 g t
          add_tac_stm g (MemoryOp(Assign(id,t)))

      | ClassLoc(e1,id) ->
          let g = add_comment g "e1.id = e0"
          let g = add_temp g
          let t0 = g.LastTemp
          let g = add_temp g
          let t1 = g.LastTemp
          let g = add_temp g
          let tp = g.LastTemp
          let g = map_expression e1 g t1
          let null_pointer_check = null_pointer_exc t1
          let g = map_expression null_pointer_check g tp
          let tjump = TJump(tp,Label.Label("NULL_POINTER_EXCEPTION"))
          let g = map_expression e0 g t0
          let asgn = MemoryOp(FieldAssignment(t1,id,t0))
          add_tac_stms g [tjump;asgn]

      | ArrayLoc(e1,e2) ->
          let g = add_comment g "e1[e2] = e0"
          let g = add_temp g
          let t0 = g.LastTemp
          let g = add_temp g
          let t1 = g.LastTemp
          let g = add_temp g
          let t2 = g.LastTemp
          let g = add_temp g
          let tp = g.LastTemp
          let g = map_expression e1 g t1
          let g = map_expression e2 g t2
          let out_of_bound_check = out_of_bound_exc t1 t2
          let g = map_expression out_of_bound_check g tp
          let fjump = FJump(tp,Label.Label("ARRAY_OUT_OF_BOUND_EXCEPTION"))
          let g = map_expression e0 g t0
          let asgn = MemoryOp(ArrayAssignment(t1,t2,t0))
          add_tac_stms g [fjump;asgn]

    | TinyJava.Ast.Statement.Call(m,actuals_opt) ->
        match m,actuals_opt with
        | ImplicitMethod(id),Some actuals ->
            let g,temps = map_actuals actuals g []
            if is_library_function id then
              let g = add_comment g "f(e1,...,en)"
              add_tac_stm g (VoidCall(None,id,temps))
            else
              let g = add_comment g "this.m(e1,...,e2)"
              add_tac_stm g (VoidCall(Some "this",id,"this" :: temps))
        | ImplicitMethod(id),None ->
            if is_library_function id then
              let g = add_comment g "f()"
              add_tac_stm g (VoidCall(None,id,[]))
            else
              let g = add_comment g "t = this.m()"
              add_tac_stm g (VoidCall(Some "this",id,["this"]))
        | MethodCall(e,id), Some actuals ->
            let g = add_comment g "id.m(e1,...,en)"
            let g = add_temp g
            let t0 = g.LastTemp
            let g = add_temp g
            let tp = g.LastTemp
            let g = map_expression e g t0
            let null_pointer_check = null_pointer_exc t0
            let g = map_expression null_pointer_check g tp
            let tjump = TJump(tp,Label.Label("NULL_POINTER_EXCEPTION"))
            let g,temps = map_actuals actuals g []
            let call = VoidCall(Some t0,id,t0 :: temps)
            add_tac_stms g [tjump;call]
        | MethodCall(e,id), None ->
            let g = add_comment g "t = id.m()"
            let g = add_temp g
            let t0 = g.LastTemp
            let g = add_temp g
            let tp = g.LastTemp
            let g = map_expression e g t0
            let null_pointer_check = null_pointer_exc t0
            let g = map_expression null_pointer_check g tp
            let tjump = TJump(tp,Label.Label("NULL_POINTER_EXCEPTION"))
            let call = VoidCall(Some t0,id,[t0])
            add_tac_stms g [tjump;call]

      | TinyJava.Ast.Return(e_opt) ->
          match e_opt with
          | Some e ->
              let g = add_comment g "Return t"
              let g = add_temp g
              let t = g.LastTemp
              let g = map_expression e g t
              add_tac_stm g (Return(t))

          | None ->
              let g = add_comment g "Return"
              add_tac_stm g VoidReturn

      | TinyJava.Ast.If(e,s1,else_opt) ->
          match else_opt with
          | None ->
              let g = add_comment g "If e then s1"
              let g = add_temp g
              let t = g.LastTemp
              let g = add_label g
              let l = g.LastLabel
              let g = map_expression e g t
              let g = add_tac_stm g (FJump(t,l))
              let g = map_statement s1 g
              add_tac_stm g (Label(l))

          | Some s2 ->
              let g = add_comment g "If e then s1 else s2"
              let g = add_temp g
              let t = g.LastTemp
              let g = add_label g
              let l_false = g.LastLabel
              let g = add_label g
              let l_end = g.LastLabel
              let g = map_expression e g t
              let g = add_tac_stm g (FJump(t,l_false))
              let g = map_statement s1 g
              let g = add_tac_stms g [Jump(l_end);Label(l_false)]
              let g = map_statement s2 g
              add_tac_stm g (Label(l_end))

      | TinyJava.Ast.While(e,s) ->
          let g = add_comment g "While e do s"
          let g = add_temp g
          let t = g.LastTemp
          let g = add_label g
          let l_begin = g.LastLabel
          let g = add_label g
          let l_end = g.LastLabel
          
          // We backup the old values of loop labels and update their value with the current labels
          let prev_loop_begin_label = g.LastLoopBeginLabel
          let prev_loop_end_label = g.LastLoopEndLabel
          g.LastLoopBeginLabel <- l_begin
          g.LastLoopEndLabel <- l_end

          let g = add_tac_stm g (Label(l_begin))
          let g = map_expression e g t
          let g = add_tac_stm g (FJump(t,l_end))
          let g = map_statement s g
          let g = add_tac_stms g [Jump(l_begin);Label(l_end)]

          // We restore the old values of loop labels
          g.LastLoopBeginLabel <- l_begin
          g.LastLoopEndLabel <- l_end

          g

      | TinyJava.Ast.Break ->
          let g = add_comment g "Break"
          add_tac_stm g (Jump(g.LastLoopEndLabel))

      | TinyJava.Ast.Continue ->
          let g = add_comment g "Continue"
          add_tac_stm g (Jump(g.LastLoopEndLabel))

      | TinyJava.Ast.Block(block) ->
          let _,stms = block
          let rec map_statements (stms : Statement list) (g : TACGenerator) : TACGenerator =
             match stms with
             | [] -> g
             | st :: sts ->
                let g = map_statement st g
                map_statements sts g
          map_statements stms g

let generate_tac (ast : Program) : TACContainer list =
  [for c in ast do
    let name,_,fm_list = c
    let methods_tac =
      let l_idx = ref 0
      [for fm in fm_list do
        match fm with
        | FieldDecl(_) -> ()
        | MethodDecl(_,m_name,_,block) ->
            let tac_generator =
              {
                      MethodName = m_name
                      GeneratedCode = []
                      TempList = []
                      LabelList = []
                      LabelIndex = !l_idx
                      TempIndex = 0
                      LastTemp = ""
                      LastLabel = Label.Label("")
                      LastLoopBeginLabel = Label.Label("")
                      LastLoopEndLabel = Label.Label("")
              }
            let rec map_block (stms : Statement list) (g : TACGenerator) : TACGenerator =
              match stms with
              | [] -> g
              | st :: sts ->
                  let g = map_statement st g
                  map_block sts g

//            let rec map_decls (defs : VarDefs) (g : TACGenerator) : TACGenerator =
//              match defs with
//              | [] -> g
//              | d :: dfs ->
//                  match d with
//                  | (id,None) ->
//                      let g = add_temp g
//                      let t = g.LastTemp
//                      {g with GeneratedCode = g.GeneratedCode @ [MemoryOp(Assign(t,id))]}
//
//                  | (id,Some e) ->
//                      let g = add_temp g
//                      let t0 = g.LastTemp
//                      let g = add_temp g
//                      let t1 = g.LastTemp
//                      let g = map_expression e g t0
//                      {g with GeneratedCode = g.GeneratedCode @ [MemoryOp(Assign(t1,t0))]}
//            
            //generate TAC for block declarations
            let _,block_statements = block

            //generate TAC for block statements
            let method_tac = map_block block_statements tac_generator
            l_idx := method_tac.LabelIndex
            yield method_tac
            ]
    let tac_container = {ClassName = name;MethodsTAC = methods_tac}
    yield tac_container
    ]

let tac_log (tacs : TACContainer list) (comments_on : bool) : Unit =
  let complete_tac_text =
    [| for tac_container in tacs do
          for method_tac in tac_container.MethodsTAC do
            let method_header = "# " + tac_container.ClassName + "." + method_tac.MethodName + " Three-Address code"
            let tac_text =
              [for tac_stm in method_tac.GeneratedCode do
                  match tac_stm with
                  | Comment(_) ->
                      if comments_on then
                        yield tac_stm.ToString()
                  | _ ->
                      yield tac_stm.ToString()]
            let tac_text = method_header :: tac_text
            //let tac_text = List.toArray tac_text 
            yield! tac_text |]
  File.WriteAllLines("tac_code.tac",complete_tac_text)

    

  
  
          